/**
 *    @file       httpservertask.cpp
 *
 *    @brief      
 *    @details
 *
 *    @version    1.0
 *    @date       02/21/2014 04:43:59 PM
 *
 *    @author     Theophilus (), wangtf418@gmail.com
 */
#include <errno.h>
#include "httpservertask.h"
#include "toolkit.h"

httpservertask::httpservertask(csocket *sock, netprocessthread *netthread, const comargs *args /* = NULL */, bf_size_t headbuflen /* = HTTP_HEAD_LEN */, bf_size_t bodybuflen /* = HTTP_BODY_LEN */)
	: servertask(sock, netthread, args)
{
	m_headbuf = NULL;
	m_max_headlen = headbuflen;
	m_body_buflen = bodybuflen;

	m_method = METHOD_UNKNOW;

	//reset_buf() called in servertask::init()
	//m_read_state = READ_HEAD;
	//m_cur_recvlen = 0;
	//m_content_len = 0;
	//m_has_recv_contentlen = 0;
}

httpservertask::~httpservertask()
{
	if (m_headbuf)
	{
		delete [] m_headbuf;
	}
}

bf_int_t httpservertask::init(bf_time_t timeout /*  = SOCKTASK_TIMEOUT */, bf_int_t sock_buflen /*  = 102400 */)
{
	if (m_max_headlen == 0 || m_body_buflen < m_max_headlen)
	{
		return BF_ERROR;
	}

	m_headbuf = new bf_char_t[m_max_headlen + 1 + m_body_buflen];
	m_headbuf[m_max_headlen] = 0;
	m_bodybuf = m_headbuf + m_max_headlen + 1;

	return servertask::init(timeout, sock_buflen);
}

bf_int_t httpservertask::proc_msg(message *msg)
{
	return BF_OK;
}

bf_int_t httpservertask::recv_process()
{
	bf_int_t ret = BF_OK;	
	
	switch (m_read_state)
	{
		case READ_HEAD:
			{
				ret = read_head();
				break;
			}
		case READ_BODY:
			{
				ret = read_body();
				break;
			}
	}

	if (ret == BF_SUCC) //recv suc
	{
		process_packets();
	}

	return BF_OK;
}

bf_int_t httpservertask::read_head()
{
	bf_int_t need_read = m_max_headlen - m_cur_recvlen;
	bf_int_t readedlen = m_socket->recv(m_headbuf + m_cur_recvlen, need_read, MSG_DONTWAIT);
	if (readedlen == 0)
	{
		return BF_ERROR;	//client close
	}
	else if (readedlen < 0)
	{
		if (errno != EINTR && errno != EAGAIN)
		{
			return BF_ERROR;
		}
		return BF_OK;
	}

	bf_size_t last_pos = m_cur_recvlen;
	m_cur_recvlen += readedlen;
	m_headbuf[m_cur_recvlen] = 0;
	last_pos = (last_pos <= 3)? 0:(last_pos - 3);
	bf_char_t *endpos = strstr(m_headbuf + last_pos, "\r\n\r\n");
	if (!endpos)
	{
		return (m_cur_recvlen == m_max_headlen)? BF_ERROR:BF_OK;
	}
	m_http_head.assign(m_headbuf, endpos + 4 - m_headbuf);

	std::string method;
	bf_size_t methodpos;
	bf_int_t ret = toolkit::get_string_by_label(m_http_head, 0, "", " ",  method, methodpos);
	if (ret != BF_OK || method.empty())
	{
		return BF_ERROR;
	}

	switch (method[0])
	{
		case 'H':
			{
				if (method == "HEAD")
				{
					m_method = METHOD_HEAD;
					return BF_SUCC;
				}
				return BF_ERROR;
				break;
			}
		case 'G':
			{
				if (method == "GET")
				{
					m_method = METHOD_GET;
					return BF_SUCC;
				}
				return BF_ERROR;
				break;
			}
		case 'P':
			{
				if (method == "POST")
				{
					m_method = METHOD_POST;
					std::string contentlen;
					bf_size_t contentlen_pos; 
					ret = toolkit::get_string_by_label(m_http_head, 0, "Content-Length:", "\r\n", contentlen, contentlen_pos, true);
					if (ret != BF_OK || contentlen.empty())
					{
						return BF_ERROR;
					}

					bf_int64_t content_len;
					ret = toolkit::str2int((bf_char_t*)contentlen.c_str(), content_len);
					if (ret != BF_OK || content_len < 0)
					{
						return BF_ERROR;
					}
					m_content_len = content_len;
					if (m_content_len + m_http_head.length() <= m_cur_recvlen)
					{
						m_has_recv_contentlen = m_content_len;
						return BF_SUCC;
					}
					else
					{
						m_has_recv_contentlen = m_cur_recvlen - m_http_head.length();
						m_cur_recvlen = m_has_recv_contentlen;
						m_bodybuf = m_headbuf + m_http_head.length();
						m_read_state = READ_BODY;
					}
				}
				return BF_ERROR;
				break;
			}
		default:
			{
				return BF_ERROR;
				break;
			}
	}
	
	return BF_OK;
}

bf_int_t httpservertask::read_body()
{
	if (m_method != METHOD_POST)
	{
		return BF_ERROR;
	}

	bf_int_t need_read = m_content_len - m_has_recv_contentlen;
	if (need_read + m_cur_recvlen > m_body_buflen)
	{
		need_read = m_body_buflen - m_cur_recvlen;
	}

	bf_int_t readedlen = m_socket->recv(m_bodybuf + m_cur_recvlen, need_read, MSG_DONTWAIT);
	if (readedlen == 0)
	{
		return BF_ERROR;	//client close
	}
	else if (readedlen < 0)
	{
		if (errno != EINTR && errno != EAGAIN)
		{
			return BF_ERROR;
		}
		return BF_OK;
	}

	m_cur_recvlen += readedlen;
	if (readedlen == need_read)
	{
		return BF_SUCC;
	}

	return BF_OK;
}

bf_int_t httpservertask::process_recv_packet(bf_char_t *buf, bf_size_t buflen)
{
	return BF_OK;
}

void httpservertask::reset_buf()
{
	m_read_state = READ_HEAD;
	m_cur_recvlen = 0;
	m_content_len = 0;
	m_has_recv_contentlen = 0;
	return;
}

bf_int_t httpservertask::process_packets()
{
	bf_int_t ret = BF_OK;
	bf_int_t cur_pos = 0;
	while (true)
	{
		switch (m_method)
		{
			case METHOD_HEAD:
				{
					ret = process_head(); 
					cur_pos += m_http_head.length();
					break;
				}
			case METHOD_GET:
				{
					ret = process_get(); 
					cur_pos += m_http_head.length();
					break;
				}
			case METHOD_POST:
				{
					if (m_read_state == READ_HEAD)
					{
						ret = process_post(m_headbuf + cur_pos, m_content_len, 0, m_content_len);
					}
					else
					{
						ret = process_post(m_bodybuf, m_cur_recvlen, m_has_recv_contentlen - m_cur_recvlen, m_content_len);
					}
					if (m_read_state == READ_HEAD)
					{
						cur_pos += m_http_head.length() + m_content_len;
					}
					break;
				}
			default:
				{
					ret = BF_ERROR;
					break;
				}
		}

		if (ret != BF_OK)
		{
			return BF_ERROR;
		}

		if (m_read_state == READ_BODY)
		{
			if (m_has_recv_contentlen == m_content_len)
			{
				reset_buf();
			}
			break;
		}

		if ((bf_size_t)cur_pos >= m_cur_recvlen)
		{
			reset_buf();
			break;
		}

		bf_char_t *endpos = strstr(m_headbuf + cur_pos, "\r\n\r\n");
		if (!endpos)
		{
			m_bodybuf = m_headbuf + m_max_headlen + 1;
			memcpy(m_bodybuf, m_headbuf + cur_pos, m_cur_recvlen - cur_pos);
			memcpy(m_headbuf, m_bodybuf, m_cur_recvlen - cur_pos);
			reset_buf();
			m_cur_recvlen = cur_pos;
			break;
		}
		m_http_head.assign(m_headbuf + cur_pos, endpos + 4 - (m_headbuf + cur_pos));

		std::string method;
		bf_size_t methodpos;
		ret = toolkit::get_string_by_label(m_http_head, 0, "", " ",  method, methodpos);
		if (ret != BF_OK || method.empty())
		{
			break;
		}
		if (method == "HEAD")
		{
			m_method = METHOD_HEAD;
		}
		else if (method == "GET")
		{
			m_method = METHOD_GET;
		}
		else if (method == "POST")
		{
			m_method = METHOD_POST;
			std::string contentlen;
			bf_size_t contentlen_pos; 
			ret = toolkit::get_string_by_label(m_http_head, 0, "Content-Length:", "\r\n", contentlen, contentlen_pos, true);
			if (ret != BF_OK || contentlen.empty())
			{
				break;
			}

			bf_int64_t content_len;
			ret = toolkit::str2int((bf_char_t*)contentlen.c_str(), content_len);
			if (ret != BF_OK || content_len < 0)
			{
				return BF_ERROR;
			}

			m_content_len = content_len;
			if (m_content_len + m_http_head.length() + cur_pos <= m_cur_recvlen)
			{
				m_has_recv_contentlen = m_content_len;
			}
			else
			{
				m_has_recv_contentlen = m_cur_recvlen - m_http_head.length() - cur_pos;
				m_cur_recvlen = m_has_recv_contentlen;
				memcpy(m_bodybuf, m_headbuf + m_http_head.length(), m_has_recv_contentlen);
				m_read_state = READ_BODY;
				break;
			}
		}
		else
		{
			ret = BF_ERROR;
			break;
		}
	}
	
	return ret;
}

bf_int_t httpservertask::process_head()
{
	return BF_OK;
}

bf_int_t httpservertask::process_get()
{
	return BF_OK;
}

bf_int_t httpservertask::process_post(bf_char_t *buf, bf_size_t buflen, bf_size_t offset, bf_size_t contentlen) 
{
	return BF_OK;
}

bf_int_t httpservertask::process_headline()
{
	bf_size_t pos = m_http_head.find("\r\n");
	if (pos == std::string::npos)
	{
		return BF_ERROR;
	}
	std::vector<bf_string_t> split;
	bf_int_t ret = toolkit::split_string_by_chars(m_http_head.c_str(), pos, " ", split);
	if (ret != BF_OK || split.size() != 3)
	{
		return BF_ERROR;
	}
	m_http_path = split[1].to_string();
	m_http_ver = split[2].to_string();
	return BF_OK;
}

